/* 
// 二十七、封住 Table表格组件

  目标：写用户列表的过程中，发现，跟其他页面的逻辑差不多，只是数据的不同
  想法：把逻辑封装复用，提高工作效率

  如果封装？
    封装逻辑：
      入参、出参(返回值)
        入参：逻辑中变化的东西
        出参：需要对外暴露的数据

  分析：
    入参：数据在变，本质是不同的请求函数，搜索用到的参数
    出参：请求回来的数据，分页的数据(pageSize, pageNum, total)

*/
import { CancelTokenSource } from "axios"
import { reactive, ref, toRefs, watch } from "vue"

interface IParams {
  pageSize?: number;
  [index: string]: any
}

// 27.49约束泛型 T 是什么
interface IResult {
  list: any[];
  total: number;
}

// 多行注释，这样子便于后期维护，鼠标以上相关的数据 useTable 也会有对应的提示
/** 
* 封装好的 表格增删改查的 hook(组合式函数)
* @param fetchCallback 请求用的函数
* @param searchParams  搜索携带的参数
* @returns {}
*/

// export default function useTable(请求的函数， 搜索用到的参数) {
// 27.1 useTable 第一个参数是请求函数，第二个是请求携带的参数，第二个参数是一个对象，因为有一些页面是多个搜索条件的
// export default function useTable(fetchCallBack: (params: any) => void, searchParams: Object) {
//27.46通过泛型把返回值传过去， fetchCallBack 返回值不是 void
// export default function useTable<T>(fetchCallBack: (params: any) => RSP<T>, searchParams: Object) {

// 27.50要求 T里面要有 IResult 的 list\total，然后这里的 params 有可能是不需要传参的话，就给个可选 ?
export default function useTable<T extends IResult>(fetchCallBack: (params?: any) => RSP<T>, searchParams: IParams) {
  // 27.3去到接口文档，查看对应的接口，去到 http 文件夹里面的 api.ts
  // 27.7声明一个响应式的数据，是表格中要渲染的数据
  // const list = ref<any[]>([])
  // 27.51这里的 any 类型约束需要换成泛型 T，然后选到对应的 list
  // 27.52去到 types 文件夹里面的 api.d.ts
  const list = ref<T['list']>([])

  // 27.26总数的做成响应式的
  const total = ref(0)

  // 27.17定义 pageNum 和 pageSize 的响应式数据
  const queryParams = reactive({
    // 当前页码
    pageNum: 1,
    // 选择一页显示多少条数据
    pageSize: searchParams.pageSize || 10
  })


  // 27.19将以下取消二次发送请求的方法，从 admin.vue 拿过来复用
  // 27.20在全局定义一个source对象，方便我们在 watch 里面使用它来取消请求
  let source: CancelTokenSource;
  // 27.21定义一个变量，标记 请求是否正在进行
  let getting = false;

  // 27.2一开始就要发送请求数据回来
  // fetchCallBack(searchParams)
  // 27.8这里写后面请求成功的逻辑
  // fetchCallBack(searchParams).then((res: any) => {
  // 27.18把 queryParams 扩展运算符到 searchParams
  // fetchCallBack({ ...queryParams, ...searchParams }).then((res: any) => {
  // 27.21将以下的方法定义一个变量存起来，然后再 return 出去
  let fetch = () => {
    // 20.22请求正在进行
    getting = true;

    // fetchCallBack({ ...queryParams, ...searchParams }).then((res: any) => {
    // 27.47那么这里的 res 就不需要 any 来约束了
    fetchCallBack({ ...queryParams, ...searchParams }).then((res) => {
      // 27.9如果请求成功
      if (res.code === 200) {
        // 27.10把数据存起来
        // 27.48这个时候会报错：Property 'list' does not exist on type 'T'，我们要继续约束泛型 T 是什么
        list.value = res.data.list
        // 27.27把总条数赋值
        // 27.28去到 role.vue 
        total.value = res.data.total
      }
    }).finally(() => {
      getting = false;
    })
  }

  // 27.23一开始先调用一次 fetch 请求数据
  fetch()

  // 27.24将分页封装的拿过来
  let { pageNum, pageSize } = toRefs(queryParams)

  watch([pageNum, pageSize], () => {
    console.log(pageNum);

    // console.log(pageSize)
    searchParams.pageSize !== undefined && (searchParams.pageSize = pageSize.value);
    if (getting) {
      getting = false;
      source.cancel();
    }
    // 27.25页码跟页容量发生变化的话，重新调用请求接口数据的方法
    fetch()

  })

  // 暴露出去的数据
  return {
    // 请求回来的数据
    // 27.11将 list 暴露出去
    // 27.12去到 role.vue 文件
    list,
    pageNum,
    pageSize,
    total,
    fetch
  }
}